using System;
using System.Collections.Generic;
using System.IO;
using System.Web;
using ExtremeSwank.Authentication.OpenID;
using ExtremeSwank.Authentication.OpenID.Plugins.Extensions;
using ExtremeSwank.Authentication.OpenID.Persistence;
using System.Web.Security;
using System.Security.Principal;
using System.Reflection;

public partial class OpenIDControl : System.Web.UI.UserControl
{
    /// <summary>
    /// Executed every time page is loaded. Checks for OpenID-related requests,
    /// and processes, if present.
    /// </summary>
    /// <param name="sender">Object invoking this method.</param>
    /// <param name="e">EventArgs associated with the request.</param>
    protected void Page_Load(object sender, EventArgs e)
    {
        Trace.TraceFinished += new TraceContextEventHandler(Trace_TraceFinished);
        if (!IsPostBack)
        {
            if (UserObject != null)
            {
                FormPanel.Visible = false;
                StatusPanel.Visible = true;
            }

            OpenIDConsumer openid = GetConsumer();
            openid.HandleResponses();
        }
    }

    /// <summary>
    /// Create an OpenIDConsumer object, and set up the event listeners.
    /// </summary>
    /// <returns>An OpenIDConsumer object.</returns>
    protected OpenIDConsumer GetConsumer()
    {
        OpenIDConsumer openid = new OpenIDConsumer();
        openid.AuthMode = AuthMode;
        openid.Identity = this.Identity;
        openid.ValidationSucceeded += new EventHandler(openid_ValidationSucceeded);
        openid.ValidationFailed += new EventHandler(openid_ValidationFailed);
        openid.ReceivedCancelResponse += new EventHandler(openid_ReceivedCancelResponse);

        return openid;
    }

    /// <summary>
    /// Handle when a user cancels the authentication request at the OpenID Provider.
    /// </summary>
    /// <param name="sender">Object invoking this method.</param>
    /// <param name="e">Eventargs associated with the request.</param>
    protected void openid_ReceivedCancelResponse(object sender, EventArgs e)
    {
        FormPanel.Visible = true;
        StatusPanel.Visible = false;
        LLabel.Text = TranslateError(Errors.RequestCancelled);
        OnRemoteCancel(e);
        OnResponseProcessed(e);
    }

    /// <summary>
    /// Handle when an authentication response has been received, and has been
    /// successfully validated.
    /// </summary>
    /// <param name="sender">Object invoking this method.</param>
    /// <param name="e">Eventargs associated with the request.</param>
    protected void openid_ValidationSucceeded(object sender, EventArgs e)
    {
        _UserObject = ((OpenIDConsumer)sender).RetrieveUser();
        FormPanel.Visible = false;
        StatusPanel.Visible = true;
        OnValidateSuccess(e);
        OnResponseProcessed(e);
        FormsAuthentication.RedirectFromLoginPage(_UserObject.Identity, false);
    }

    /// <summary>
    /// Handle when an authentication response has been received, but 
    /// validation failed.
    /// </summary>
    /// <param name="sender">Object invoking this method.</param>
    /// <param name="e">Eventargs associated with the request.</param>
    protected void openid_ValidationFailed(object sender, EventArgs e)
    {
        FormPanel.Visible = true;
        StatusPanel.Visible = false;
        LLabel.Text = TranslateError(((OpenIDConsumer)sender).GetError());
        OnValidateFail(e);
        OnResponseProcessed(e);
    }

    /// <summary>
    /// Event fires upon successful validation received from 
    /// Identity Provider.
    /// </summary>
    public event EventHandler ValidateSuccess;

    /// <summary>
    /// Fires when successful validation is received from
    /// Identity Provider.
    /// </summary>
    /// <param name="e">EventArgs</param>
    protected virtual void OnValidateSuccess(EventArgs e)
    {
        if (ValidateSuccess != null)
        {
            ValidateSuccess(this, e);
        }
    }

    /// <summary>
    /// Event fires when unsuccessful validation is received frmo
    /// Identity Provider
    /// </summary>
    public event EventHandler ValidateFail;

    /// <summary>
    /// Fires when unsuccessful validation is received from
    /// Identity Provider
    /// </summary>
    /// <param name="e">EventArgs</param>
    protected virtual void OnValidateFail(EventArgs e)
    {
        if (ValidateFail != null)
        {
            ValidateFail(this, e);
        }
    }
    /// <summary>
    /// Event fires when user cancels request at Identity Provider
    /// and is redirected back to this application.
    /// </summary>
    public event EventHandler RemoteCancel;

    /// <summary>
    /// Fires when user cancels request at Identity Provider
    /// and is redirected back to this application.
    /// </summary>
    /// <param name="e"></param>
    protected virtual void OnRemoteCancel(EventArgs e)
    {
        if (RemoteCancel != null)
        {
            RemoteCancel(this, e);
        }
    }

    /// <summary>
    /// Event fires after user has submitted login form,
    /// but before performing authentication-related functions.
    /// </summary>
    public event EventHandler Login;

    /// <summary>
    /// Fires after user has submitted login form, but
    /// before performing authentication-related functions.
    /// </summary>
    /// <param name="e">EventArgs</param>
    protected virtual void OnLogin(EventArgs e)
    {
        if (Login != null)
        {
            Login(this, e);
        }
    }

    /// <summary>
    /// Event fires when a OpenID response is received and processing has completed.
    /// </summary>
    public event EventHandler ResponseProcessed;

    protected virtual void OnResponseProcessed(EventArgs e)
    {
        if (ResponseProcessed != null)
        {
            ResponseProcessed(this, e);
        }
    }

    /// <summary>
    /// Event fires after user has used the "log out" function.
    /// </summary>
    public event EventHandler Logout;

    /// <summary>
    /// Fires after user has used the "log out" function.
    /// </summary>
    /// <param name="e">EventArgs</param>
    protected virtual void OnLogout(EventArgs e)
    {
        if (Logout != null)
        {
            Logout(this, e);
        }
    }

    /// <summary>
    /// Sets the authentication mode to be used. 
    /// Supports either "stateful" or "stateless".
    /// Defaults to "stateful".
    /// </summary>
    public AuthenticationMode AuthMode
    {
        get
        {
            if (ViewState["AuthMode"] == null) { return AuthenticationMode.Stateful; }
            return (AuthenticationMode)ViewState["AuthMode"];
        }
        set
        {
            ViewState["AuthMode"] = value;
        }
    }

    /// <summary>
    /// The URL of the OpenID Provider to authenticate against.  This will result in the user being prompted for the desired
    /// OpenID by the OpenID Provider.
    /// </summary>
    /// <remarks>
    /// Setting this property to a non-blank value will result in the Identity being ignored.
    /// </remarks>
    public string OpenIDServerURL
    {
        get
        {
            if (ViewState["UseDirectedIdentity"] == null) { return null; }
            return (string)ViewState["UseDirectedIdentity"];
        }
        set
        {
            ViewState["UseDirectedIdentity"] = value;
        }
    }

    /// <summary>
    /// From Simple Registration Extension. Comma-delimited list of Simple Registration
    /// fields that the Identity Provider should require the user to provide.
    /// </summary>
    public string RequiredFields
    {
        get { return (string)ViewState["RequiredFields"]; }
        set { ViewState["RequiredFields"] = value; }
    }

    /// <summary>
    /// From Simple Registration Extension. Comma-delimited list of Simple Registration
    /// fields that the Identity Provider can optionally ask the user to provide.
    /// </summary>
    public string OptionalFields
    {
        get { return (string)ViewState["OptionalFields"]; }
        set { ViewState["OptionalFields"] = value; }
    }

    /// <summary>
    /// From Simple Registration Extension. URL of this site's privacy policy to send
    /// to the Identity Provider.
    /// </summary>
    public string PolicyURL
    {
        get { return (string)ViewState["Identity"]; }
        set { ViewState["Identity"] = value; }
    }

    /// <summary>
    /// Optional. Base URL of this site. Sets the scope of the authentication request. 
    /// </summary>
    public string Realm
    {
        get { return (string)ViewState["TrustRoot"]; }
        set { ViewState["TrustRoot"] = value; }
    }

    /// <summary>
    /// OpenID identitier.
    /// </summary>
    private string Identity
    {
        get { return (string)Session["OpenID_Identity"]; }
        set { Session["OpenID_Identity"] = value; }
    }

    private OpenIDUser _UserObject
    {
        get { return (OpenIDUser)Session["UserObject"]; }
        set { Session["UserObject"] = value; }
    }
    /// <summary>
    /// OpenIDUser object that represents the authenticated user and all
    /// information received from the Identity Provider.
    /// </summary>
    public OpenIDUser UserObject
    {
        get { return _UserObject; }
    }

    /// <summary>
    /// User has clicked the login button. Sets up a new OpenIDConsumer
    /// object and begins the authentication sequence. 
    /// Fires the OnLogin event. 
    /// </summary>
    /// <param name="sender">Object invoking this method.</param>
    /// <param name="e">EventArgs related to this request.</param>
    protected void Button_Click(object sender, EventArgs e)
    {
        OpenIDConsumer openid = GetConsumer();
        SimpleRegistration sr = new SimpleRegistration(openid);
        if (!String.IsNullOrEmpty(this.RequiredFields)) { sr.RequiredFields = this.RequiredFields; }
        if (!String.IsNullOrEmpty(this.OptionalFields)) { sr.OptionalFields = this.OptionalFields; }
        if (!String.IsNullOrEmpty(this.PolicyURL)) { sr.PolicyURL = this.PolicyURL; }

        if (String.IsNullOrEmpty(OpenIDServerURL))
        {
            openid.Identity = openid_url.Text;
            this.Identity = openid.Identity;
        }
        else
        {
            openid.UseDirectedIdentity = true;
            openid.OpenIDServer = OpenIDServerURL;
        }

        OnLogin(e);
        openid.BeginAuth();

        if (openid.IsError())
        {
            LLabel.Text = TranslateError(openid.GetError());
        }
        else
        {
            LLabel.Text = "";
        }
    }
    /// <summary>
    /// User has clicked the "log out" button. Removes all information
    /// about the user from the Session state.
    /// </summary>
    /// <param name="sender">Object invoking this method.</param>
    /// <param name="e">EventArgs associated with the request.</param>
    protected void LogOutButton_Click(object sender, EventArgs e)
    {
        OnLogout(e);
        HttpContext.Current.Session.Clear();
        FormPanel.Visible = true;
        StatusPanel.Visible = false;
        FormsAuthentication.SignOut();
        FormsAuthentication.RedirectToLoginPage();
    }
    /// <summary>
    /// Creates a new instance of OpenIDControl.
    /// </summary>
    public OpenIDControl()
    {
    }

    /// <summary>
    /// Translate errors received from Consumer to user-visible text.
    /// </summary>
    /// <param name="error">Value from Errors enumeration</param>
    /// <returns>A user-visible string matching the error</returns>
    public string TranslateError(Errors error)
    {
        switch (error)
        {
            case Errors.NoErrors:
                return null;
            case Errors.HttpError:
                return "Connection to OpenID Provider failed. Please try again later.";
            case Errors.NoIdSpecified:
                return "Please specify an OpenID.";
            case Errors.NoServersFound:
                return "Unable to locate OpenID Provider. Double-check your OpenID.";
            case Errors.NoStatelessImmediate:
                return "Immediate-mode does not support Stateless authentication.";
            case Errors.RequestRefused:
                return "OpenID Provider refused authentication request.";
            case Errors.SessionTimeout:
                return "Session timed out. Please try again.";
            case Errors.RequestCancelled:
                return "Login request cancelled.";
            default:
                return null;
        }
    }

    /// <summary>
    /// If tracing is enabled, write a OpenID-specific log file to the current directory
    /// upon processing completion.
    /// </summary>
    /// <param name="sender">Object invoking this method.</param>
    /// <param name="e">Eventargs associated with this request.</param>
    void Trace_TraceFinished(object sender, TraceContextEventArgs e)
    {
        List<string> list = new List<string>();
        list.Add("Timestamp: " + DateTime.Now);
        foreach (TraceContextRecord tcr in e.TraceRecords)
        {
            if (tcr.Category == "openid")
            {
                list.Add(tcr.Message);
            }
        }
        list.Add("--------------------");
        string alltext = String.Join("\n", list.ToArray()) + "\n";
        File.AppendAllText(Server.MapPath("~/openid.log"), alltext);
        alltext = null;
        list = null;
    }
}
